package com.jml.config;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jml.constant.ConfigMd5;
import com.jml.constant.Constant;
import com.jml.scope.RefreshScope;
import com.jml.service.ConfigClientService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.StandardEnvironment;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class StartReadConfiguration implements EnvironmentAware, ApplicationListener<ContextRefreshedEvent> {

    private StandardEnvironment environment;

    private JmlBeanDefinitionRegistry jmlBeanDefinitionRegistry;

    private BeanDefinitionRegistry beanDefinitionRegistry;

    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @Autowired
    private ConfigClientService configClientService;

    private ScheduledExecutorService scheduledService = Executors.newSingleThreadScheduledExecutor();

    /**
     * 获取环境变量和spring的配置文件的类
     *
     * @param environment
     */
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = (StandardEnvironment) environment;
    }

    /**
     * 容器初始化完毕后执行
     *
     * @param event
     */
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        init();
    }

    public void init() {
        jmlBeanDefinitionRegistry = (JmlBeanDefinitionRegistry)
                applicationContext.getBeanFactory().getBean("jmlBeanDefinitionRegistry");
        beanDefinitionRegistry = jmlBeanDefinitionRegistry.getBeanDefinitionRegistry();
        MutablePropertySources propertySources = environment.getPropertySources();
        propertySources.forEach(propertySource -> {
            if (propertySource instanceof OriginTrackedMapPropertySource) {
                OriginTrackedMapPropertySource originTrackedMapPropertySource
                        = (org.springframework.boot.env.OriginTrackedMapPropertySource) propertySource;
                //从云端读取当前项目的配置文件
                JSONObject configInfo = configClientService.getConfig();
                if (configInfo == null) {
                    return;
                }
                //读取配置文件
                JSONArray config = configInfo.getJSONArray("config");
                for (int i = 0; i < config.size(); i++) {
                    JSONObject jsonObject = config.getJSONObject(i);
                    String key = jsonObject.getString("key");
                    String value = jsonObject.getString("value");
                    //把配置文件信息放入内存中
                    originTrackedMapPropertySource.getSource().put(key, value);
                }
                // 得到配置文件后，刷新配置，把旧对象删除，然后创建新对象，新对象这个时候可以获取所有的配置文件
                // 旧对象在创建的时候是没有配置文件的，或者哪个时候的配置文件和现在的配置文件不一样,所以需要重新创建。
                // 项目启动时 读取到配置文件 将md5值保存
                String md5 = configInfo.getString("md5");
                refreshBean(md5);
            }
        });
        // 开启定时任务检查配置文件是否有发生变化
        scheduledService.scheduleAtFixedRate(new CheckConfigurationFileThread(), 3, 3, TimeUnit.SECONDS);

    }


    private void refreshBean(String md5) {
        //获取所有BD的名字，遍历每一个BD，如果类上有jmlRefresh的scope就进行类的销毁，然后重新创建一下。
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanDefinitionName);
            if (Constant.SCOPE_NAME.equals(beanDefinition.getScope())) {
                //首次进来的时候，注解为@Scope(Constant.SCOPE_NAME)的类还没有初始化
                applicationContext.getBeanFactory().destroyBean(beanDefinitionName);
                // 卸载该bean
                RefreshScope.getBeans().remove(beanDefinitionName);
                //重新根据BD加载这个Bean
                Object bean = applicationContext.getBean(beanDefinitionName);
            }
        }
        ConfigMd5.setMd5(md5);
    }


    class CheckConfigurationFileThread implements Runnable {

        @Override
        public void run() {
            JSONObject result = configClientService.md5Comparison();
            Integer code = result.getInteger("code");
            if (code != 201) {
                return;
            }
            MutablePropertySources propertySources = environment.getPropertySources();
            propertySources.forEach(propertySource -> {
                if (propertySource instanceof OriginTrackedMapPropertySource) {
                    OriginTrackedMapPropertySource originTrackedMapPropertySource
                            = (org.springframework.boot.env.OriginTrackedMapPropertySource) propertySource;
                    JSONObject configInfo = configClientService.getConfig();
                    if (configInfo == null) {
                        return;
                    }
                    JSONArray config = configInfo.getJSONArray("config");
                    for (int i = 0; i < config.size(); i++) {
                        JSONObject jsonObject = config.getJSONObject(i);
                        String key = jsonObject.getString("key");
                        String value = jsonObject.getString("value");
                        originTrackedMapPropertySource.getSource().put(key, value);
                    }
                    refreshBean(result.getJSONObject("data").getString("md5"));
                    // 刷新配置
                    ConfigMd5.setMd5(result.getJSONObject("data").getString("md5"));
                }
            });
        }
    }
}
